package com.zxy.supplier_system.server.service

import com.zxy.supplier_system.server.controller.CheckInventoryRecordController
import com.zxy.supplier_system.server.controller.CheckInventoryRecordsItemController
import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.repository.*
import com.zxy.supplier_system.server.utils.*
import org.springframework.core.io.InputStreamResource
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.server.ResponseStatusException
import java.time.LocalDate
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter

@Service
class CheckInventoryRecordService(
    private val checkInventoryRecordRepository: CheckInventoryRecordRepository,
    private val checkInventoryRecordItemRepository: CheckInventoryRecordItemRepository,
    private val employeeRepository: EmployeeRepository,
    private val systemServiceService: SystemServiceService,
    private val goodsRepository: GoodsRepository,
    private val employeeService: EmployeeService,
    private val employeeOperateRecordService: EmployeeOperateRecordService,
) {

    @Transactional(readOnly = true)
    fun queryCheckInventoryRecord(
        queryCheckInventoryRecordRequest: CheckInventoryRecordController.QueryCheckInventoryRecordRequest,
        pageable: Pageable
    ): Page<CheckInventoryRecordController.QueryCheckInventoryRecordResponse> {

        val employee = employeeService.getRequiredCurrentEmployee()
        return this.checkInventoryRecordRepository.findAll(
            Specification.allOf(
                CheckInventoryRecordRepository.Specifications.endDateLessThanOrEqualTo(queryCheckInventoryRecordRequest.endDate),
                CheckInventoryRecordRepository.Specifications.startDateGreaterThanOrEqualTo(
                    queryCheckInventoryRecordRequest.startDate
                ),
                CheckInventoryRecordRepository.Specifications.employeeIn(queryCheckInventoryRecordRequest.employeeIds),
                CheckInventoryRecordRepository.Specifications.submittedEquals(queryCheckInventoryRecordRequest.submitted),
            ).and(
                if (employee.authorities.contains(Authority.卖场_订单_查询所有)) {
                    Specification.where(null)
                } else {
                    CheckInventoryRecordRepository.Specifications.marketSaleManagerEqual(employee.id!!)
                        .or(CheckInventoryRecordRepository.Specifications.marketShoppingGuidesContains(employee.id!!))
                }
            ), pageable
        ).map { checkInventoryRecord ->
            CheckInventoryRecordController.QueryCheckInventoryRecordResponse(
                checkInventoryRecord.id,
                checkInventoryRecord.createdBy.let {
                    IntIdAndName(it.id!!, it.name)
                },
                checkInventoryRecord.createdDateTime,
                checkInventoryRecord.items.size,
                checkInventoryRecord.name,
                checkInventoryRecord.submittedDateTime,
            )
        }
    }

    @Transactional
    fun createCheckInventoryRecord(request: CheckInventoryRecordController.CreateCheckInventoryRecordRequest): CheckInventoryRecordController.QueryCheckInventoryRecordResponse {
        val checkInventoryRecord = this.checkInventoryRecordRepository.save(CheckInventoryRecord().apply {
            this.name = request.name ?: (LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "盘点")
        })
        this.employeeOperateRecordService.createCheckInventoryRecord(checkInventoryRecord)
        return CheckInventoryRecordController.QueryCheckInventoryRecordResponse(
            checkInventoryRecord.id,
            checkInventoryRecord.createdBy.let {
                IntIdAndName(it.id!!, it.name)
            },
            checkInventoryRecord.createdDateTime,
            0,
            checkInventoryRecord.name,
            null
        )
    }

    @Transactional
    fun updateOrderGoods(
        id: Int,
        updateOrderGoodsRequest: CheckInventoryRecordController.UpdateOrderGoodsRequest
    ) {
        val checkInventoryRecord = this.checkInventoryRecordRepository.findByIdOrNull(id).orElse404()
        checkInventoryRecord.items.clear()
        checkInventoryRecord.items.addAll(
            updateOrderGoodsRequest.items.mapIndexed { index, it ->
                CheckInventoryRecordItem().apply {
                    this.record = checkInventoryRecord
                    this.amount = it.amount
                    this.goods =
                        goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(it.id, systemServiceService.getCurrentSystemService())
                            .orElse404()
                    this.sequence = index
                }
            }
        )
        this.checkInventoryRecordRepository.save(checkInventoryRecord)
    }

    @Transactional(readOnly = true)
    fun getCheckInventoryRecordDetail(
        id: Int,
        includeItems: Boolean
    ): CheckInventoryRecordController.CheckInventoryRecordDetail {
        val checkInventoryRecord = this.checkInventoryRecordRepository.findByIdOrNull(id).orElse404()
        return CheckInventoryRecordController.CheckInventoryRecordDetail(
            checkInventoryRecord.id,
            checkInventoryRecord.createdDateTime,
            checkInventoryRecord.name,
            IntIdAndName(checkInventoryRecord.createdBy.id!!, checkInventoryRecord.createdBy.name),
            if (!includeItems) emptyList() else checkInventoryRecord.items.map { item ->
                val goods = item.goods
                goods.let { it ->
                    CheckInventoryRecordController.CheckInventoryRecordItemResponse(
                        it.id,
                        it.name, it.barcode,
                        it.price,
                        it.amount,
                        it.createdDateTime,
                        it.lastModifiedDateTime,
                        it.brand?.toIntIdAndName(),
                        it.category?.toIntIdAndName(),
                        item.amount,
                        it.boxSpecification,
                    )
                }
            },
            checkInventoryRecord.submittedDateTime,
        )
    }

    @Transactional
    fun addItem(
        orderId: Int,
        addCheckInventoryRecordsItemRequest: CheckInventoryRecordsItemController.AddCheckInventoryRecordsItemRequest
    ) {
        val checkInventoryRecord = this.checkInventoryRecordRepository.findByIdOrNull(orderId).orElse404()
        checkInventoryRecord.items.add(CheckInventoryRecordItem().apply {
            this.record = checkInventoryRecord
            this.goods = goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(
                addCheckInventoryRecordsItemRequest.goodsId,
                systemServiceService.getCurrentSystemService()
            ).orElse404()
            this.amount = addCheckInventoryRecordsItemRequest.amount
            this.subGoods =
                if (addCheckInventoryRecordsItemRequest.subGoodsId == null) null else goods.subGoodsList.find { it.id == addCheckInventoryRecordsItemRequest.subGoodsId }
            this.sequence = checkInventoryRecord.items.nextSequence
        })
        this.checkInventoryRecordRepository.save(checkInventoryRecord)
        this.employeeOperateRecordService.addCheckInventoryRecordItem(checkInventoryRecord)
    }

    @Transactional
    fun deleteItem(orderId: Int, goodsId: Int) {
        this.checkInventoryRecordItemRepository.deleteByRecord_IdAndGoods_IdAndGoods_SystemServiceAndSubGoods_Id(
            orderId,
            goodsId,
            systemServiceService.getCurrentSystemService(),
            null
        )
    }

    @Transactional
    fun deleteItem(orderId: Int, goodsId: Int, subGoodsId: Int) {
        this.checkInventoryRecordItemRepository.deleteByRecord_IdAndGoods_IdAndGoods_SystemServiceAndSubGoods_Id(
            orderId,
            goodsId,
            systemServiceService.getCurrentSystemService(),
            subGoodsId
        )
    }

    @Transactional
    fun updateItemAmount(
        goodsId: Int,
        recordId: Int,
        updateItemAmountRequest: CheckInventoryRecordsItemController.UpdateItemAmountRequest,
        subGoodsId: Int? = null
    ) {
        val checkInventoryRecordItem =
            this.checkInventoryRecordItemRepository.findByGoods_SystemServiceAndGoods_IdAndRecord_IdAndSubGoods_Id(
                systemServiceService.getCurrentSystemService(),
                goodsId,
                recordId,
                subGoodsId
            ).orElse404()
        checkInventoryRecordItem.amount = updateItemAmountRequest.amount
        this.checkInventoryRecordItemRepository.save(checkInventoryRecordItem)
        this.employeeOperateRecordService.updateCheckInventoryRecordItemAmount(checkInventoryRecordItem)
    }

    fun getAllEmployees(): List<IntIdAndName> {
        return this.employeeRepository.findAll(
            EmployeeRepository.Specifications.haveCreatedCheckInventoryRecord().and(
                SystemServiceRepository.Specifications.systemServiceEquals(
                    { root -> root },
                    systemServiceService.getCurrentSystemService()
                )
            )
        ).map {
            IntIdAndName(it.id!!, it.name)
        }
    }


    fun queryOrderItemsInOrder(
        orderId: Int,
        pageable: Pageable
    ): Page<CheckInventoryRecordsItemController.CheckInventoryRecordItemSummary> {
        return this.checkInventoryRecordItemRepository.findAllByRecord_IdOrderBySequenceAsc(orderId, pageable).map {
            val goods = it.goods
            CheckInventoryRecordsItemController.CheckInventoryRecordItemSummary.fromGoodsAndAmount(goods, it.amount)
        }
    }

    fun exists(orderId: Int, goodsId: Int): Boolean {
        return this.checkInventoryRecordItemRepository.existsByRecord_IdAndGoods_Id(orderId, goodsId)
    }

    fun exists(orderId: Int, barcode: String): Boolean {
        return this.checkInventoryRecordItemRepository.existsByRecord_IdAndGoods_Barcode(orderId, barcode)
    }


    @Transactional
    fun addItem(orderId: Int, barcode: String): CheckInventoryRecordsItemController.CheckInventoryRecordItemSummary {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val checkInventoryRecord = this.checkInventoryRecordRepository.findByIdAndCreatedBy_SystemService(
            orderId,
            currentSystemService
        ).orElse404()
        val goods =
            goodsRepository.findByBarcodeAndSystemServiceAndDisableIsFalse(barcode, currentSystemService)
                .orElse404()
        return orderItemInOrderSummary(checkInventoryRecord, goods)
    }

    @Transactional
    fun submitCheckInventoryRecord(id: Int) {
        val checkInventoryRecord = this.checkInventoryRecordRepository.findByIdAndCreatedBy_SystemService(
            id,
            systemServiceService.getCurrentSystemService()
        ).orElse404()
        if (checkInventoryRecord.submittedDateTime == null) {
            checkInventoryRecord.submittedDateTime = OffsetDateTime.now()
            checkInventoryRecord.items.forEach {
                it.goods.amount += it.amount
            }
            this.goodsRepository.saveAll(checkInventoryRecord.items.map { it.goods })
            this.checkInventoryRecordRepository.save(checkInventoryRecord)
            this.employeeOperateRecordService.submitCheckInventoryRecord(checkInventoryRecord)
        } else {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST)
        }
    }

    @Transactional
    fun addItem(orderId: Int, goodsId: Int): CheckInventoryRecordsItemController.CheckInventoryRecordItemSummary {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val checkInventoryRecord = this.checkInventoryRecordRepository.findByIdAndCreatedBy_SystemService(
            orderId,
            currentSystemService
        ).orElse404()
        val goods =
            goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(goodsId, currentSystemService)
                .orElse404()
        return orderItemInOrderSummary(checkInventoryRecord, goods)
    }

    private fun orderItemInOrderSummary(
        checkInventoryRecord: CheckInventoryRecord,
        goods: Goods
    ): CheckInventoryRecordsItemController.CheckInventoryRecordItemSummary {
        val amount = 1
        checkInventoryRecord.items.add(CheckInventoryRecordItem().apply {
            this.record = checkInventoryRecord
            this.goods =
                goods
            this.amount = amount
            this.sequence = checkInventoryRecord.items.nextSequence
        })
        this.checkInventoryRecordRepository.save(checkInventoryRecord)
        this.employeeOperateRecordService.addCheckInventoryRecordItem(checkInventoryRecord)
        return CheckInventoryRecordsItemController.CheckInventoryRecordItemSummary.fromGoodsAndAmount(goods, amount)
    }

    fun getAllCheckInventoryRecordCreatedDateRange(): NullableLocalDateRange {
        return NullableLocalDateRange(
            this.checkInventoryRecordRepository.findMaxCreatedDateTime()?.toLocalDate(),
            this.checkInventoryRecordRepository.findMaxCreatedDateTime()?.toLocalDate()
        )
    }

    @Transactional(readOnly = true)
    fun exportToExcel(id: Int, fields: Array<GoodsExcelField>): InputStreamResource {
        val checkInventoryRecord: CheckInventoryRecord =
            this.checkInventoryRecordRepository.findByIdOrNull(id).orElse404()
        val items: MutableList<CheckInventoryRecordItem> = checkInventoryRecord.items
        val workbook = exportGoodsDataToWorkbook(fields, items.map { item -> item.goodsExcelRow })
        return workbookToInputStreamResource(workbook)
    }

}